Lektion 18



Quadrics

Quadrics sind eine Möglichkeit komplexe Objekte zu zeichen, die normalerweise mehrere FOR-Schleifen und einiges Hintergrundwissen in Trigonometrie benötigen würden.

Wir werden den Code aus Lektion sieben verwenden. Wir werden sieben Variablen hinzufügen und die Texturen ein wenig verändern um etwas Abwechslung hinein zu bringen :)

#include < windows.h>					// Header Datei für Windows
#include < stdio.h>					// Header Datei für Standard Eingabe/Ausgabe
#include < gl\gl.h>					// Header Datei für die OpenGL32 Library
#include < gl\glu.h>					// Header Datei für die GLu32 Library
#include < gl\glaux.h>					// Header Datei für die GLaux Library

HDC		hDC=NULL;				// Privater GDI Device Context
HGLRC		hRC=NULL;				// Permanenter Rendering Context
HWND		hWnd=NULL;				// Enthält unser Fenster-Handle
HINSTANCE	hInstance;				// Enthält die Instanz der Applikation

bool	keys[256];					// Array das für die Tastatur Routine verwendet wird
bool	active=TRUE;					// Fenster Aktiv Flag standardmäßig auf TRUE gesetzt
bool	fullscreen=TRUE;				// Fullscreen Flag ist standardmäßig auf TRUE gesetzt
bool	light;						// Beleuchtung AN/AUS
bool	lp;						// L gedrückt?
bool	fp;						// F gedrückt?
bool    sp;						// Leertaste gedrückt?	( NEU )

int	part1;						// Start der Disc	( NEU )
int	part2;						// Ende der Disc	( NEU )
int	p1=0;						// Increase 1		( NEU )
int	p2=1;						// Increase 2		( NEU )

GLfloat	xrot;						// X Rotation
GLfloat	yrot;						// Y Rotation
GLfloat xspeed;						// X Rotation Geschwindigkeit
GLfloat yspeed;						// Y Rotation Geschwindigkeit

GLfloat	z=-5.0f;					// Tiefe in den Bildschirm

GLUquadricObj *quadratic;				// Speicher für unsere Quadratic Objecte ( NEU )

GLfloat LightAmbient[]=  { 0.5f, 0.5f, 0.5f, 1.0f };	// Ambiente Licht Werte
GLfloat LightDiffuse[]=	 { 1.0f, 1.0f, 1.0f, 1.0f };	// Diffuse Licht Werte
GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };	// Licht Position

GLuint	filter;						// Welcher Filter benutzt werden soll
GLuint	texture[3];					// Speicher für 3 Texturen
GLuint  object=0;					// Welches Object gezeichnet werden soll ( NEU )

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);	// Deklaration für WndProc

Okay, gehen Sie nun runter zu IniGL(). Wir fügen hier 3 Zeilen Code ein, um unser Qudaratic zu initialisieren. Fügen Sie die 3 Zeilen hinter dem Teil ein, wo Sie light1 aktiveren, aber noch bevor Sie True zurückgeben. Die erste Codezeile initialisiert den Quadratic und erzeugt einen Zeiger auf den Speicherbereich, wo er sich befinden wird. Wenn er nicht erzeugt werden kann, wird 0 zurückgegeben. Die zweite Codezeile erzeugt die Normalen für das Quadratic, so das die Beleuchtugn gut aussieht. Andere mögliche Werte währen GLU_NONE udn GLU_FLAT. Als letztes aktiveren Textur-Mapping für unser Quadratic. Textur-Mapping ist irgendwie ziemlich linkisch und funktioniert nie so, wie sie sich es vorstellen, so wie sie es schon von der Kisten-Textur kennen.

	quadratic=gluNewQuadric();			// erzeugt einen Zeiger auf das Quadric Obkect ( NEU )
	gluQuadricNormals(quadratic, GLU_SMOOTH);	// erzeugt Normalen ( NEU )
	gluQuadricTexture(quadratic, GL_TRUE);		// erzeugt Textur Koordinaten ( NEU )

Nun habe ich mich entschlossen, den Würfel in diesem Tutorial beizubehalten, so dass Sie sehen können, wie Texturen auf Qudratic-Objekte gemapped werden. Ich habe mich entschlossen den Würfel in eine eigen Funktion zu verschieben, so das die Zeichnen-Funktion beim neu schreiben sauberer aussieht. Jeder sollte diesen Code wiedererkennen. =P

GLvoid glDrawCube()					// Zeichne einen Würfel
{
		glBegin(GL_QUADS);			// Beginne Qudrate zu zeichnen
		// Front Face
		glNormal3f( 0.0f, 0.0f, 1.0f);		// Normalen die forwärts zeigen
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Unten Links der Textur und des Qudrats
		glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Unten rechts der Textur und des Qudrats
		glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Oben rechts der Textur und des Qudrats
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Oben Links der Textur und des Qudrats
		// Back Face
		glNormal3f( 0.0f, 0.0f,-1.0f);		// Normal Facing Away
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Unten rechts der Textur und des Qudrats
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Oben rechts der Textur und des Qudrats
		glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Oben Links der Textur und des Qudrats
		glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Unten Links der Textur und des Qudrats
		// Top Face
		glNormal3f( 0.0f, 1.0f, 0.0f);		// Normal Facing Up
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Oben Links der Textur und des Qudrats
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Unten Links der Textur und des Qudrats
		glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Unten rechts der Textur und des Qudrats
		glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Oben rechts der Textur und des Qudrats
		// Bottom Face
		glNormal3f( 0.0f,-1.0f, 0.0f);		// Normal Facing Down
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Oben rechts der Textur und des Qudrats
		glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Oben Links der Textur und des Qudrats
		glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Unten Links der Textur und des Qudrats
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Unten rechts der Textur und des Qudrats
		// Right face
		glNormal3f( 1.0f, 0.0f, 0.0f);		// Normal Facing Right
		glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Unten rechts der Textur und des Qudrats
		glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Oben rechts der Textur und des Qudrats
		glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Oben Links der Textur und des Qudrats
		glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Unten Links der Textur und des Qudrats
		// Left Face
		glNormal3f(-1.0f, 0.0f, 0.0f);		// Normal Facing Left
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Unten Links der Textur und des Qudrats
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Unten rechts der Textur und des Qudrats
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Oben rechts der Textur und des Qudrats
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Oben Links der Textur und des Qudrats
	glEnd();					// Done Drawing Quads
}

Als nächstes ist die DrawGLScene-Funktion dran, wo ich eine einfache IF-Abfrage benutzt habe, um die einzelnen Objekte zu zeichnen. Außerdem habe ich eine statische Variable benutzt (eine lokale Variable, welche ihren Wert jeweils behält wenn sie aufgerufen wird), um einen coolen Effekt zu erzielen, wenn Teile der Disk gezeichnet werden. Ich schreibe die komplette DrawGLScene-Funktion der Übersicht halber komplett neu.

Sie werden bemerken, wenn ich über die benutzten Parameter spreche, ignoriere ich den eigentlichen ersten Parameter (quadratic). Dieser Parameter wird für alle Objekte benutzt, die wir neben dem Würfel zeichnen, weshalb ich ihn ignoriere, wenn ich über die Parameter spreche.

int DrawGLScene(GLvoid)						// Hier kommt der ganze Zeichnen-Kram hin
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Löscht den Bildschirm und den Depth-Buffer
	glLoadIdentity();					// Resettet die Ansicht (View)
	glTranslatef(0.0f,0.0f,z);				// In den Bildschirm hineinbewegen

	glRotatef(xrot,1.0f,0.0f,0.0f);				// Rotiere auf der X-Achse 
	glRotatef(yrot,0.0f,1.0f,0.0f);				// Rotiere auf der X-Achse

	glBindTexture(GL_TEXTURE_2D, texture[filter]);		// Wähle eine Filter-Textur

	// Dieser Code-Ausschnitt ist neu ( NEU )
	switch(object)						// Überprüfe object um herauszufinden, was gezeichnet werden soll
	{
	case 0:							// Zeichne Objekt 1
		glDrawCube();					// Zeichne unseren Würfel
		break;						// Fertig

Das zweite Objekt, das wir erzeugen, wird ein Zylinder sein. Der erste Parameter (1.0f) ist der Radius des Zylinders an seiner Basis (Boden). Der zweite Parameter (1.0f) ist der Radius des Zylinders an der Spitze. der dritte Parameter (3.0f) ist die Höhe des Zylinders (wie lang er ist). Der vierte Parameter (32) gibt an, wieviele Unterteilungen um die Z-Achse "herum" sind und letztendlich der fünfte Parameter (32) enthält die Anzahl der Unterteilungen "entlängs" der Z-Achse. Je mehr Unterteilungen es gibt, desto detailierter ist das Objekt. Indem Sie die Anzahl der Unterteilungen erhöhen, fügen Sie mehr Polygone dem Objekt hinzu. Demnach kaufen Sie sich Qualität mittel Geschwindigkeitseinbußen. Meistens ist es recht einfach ein gesundes Mittelmaß zu finden.

	case 1:							// Zeichne Objekt 2
		glTranslatef(0.0f,0.0f,-1.5f);			// Zentriere den Zylinder
		gluCylinder(quadratic,1.0f,1.0f,3.0f,32,32);	// Zeichne unseren Zylinder
		break;						// Fertig
 

Das dritte Objekt das wir erzeugen hat die Form einer CD. Der erste Parameter (0.5f) ist der innere Radius der Disk. Dieser Wert kann gleich null sein, wenn kein Loch in der Mitte gewünscht ist. Je größer der innere Radius, desto größer ist das Loch in der Mitte der Disc. Der zweite Parameter (1.5f) ist der äußere Radius. Dieser Wert sollte größer sein, als der innere Radius. Wenn Sie ihn ein klein wenig größer als den inneren Radius machen, erhalten Sie einen dünnen Ring. Wenn Sie ihn wesentlich größer machen, werden Sie einen dicken Ring erhalten. Der dritte Parameter (32) ist die Anzahl der Scheiben aus der die Disk besteht. Denken Sie bei Scheiben an Pizza-Stücke. Die mehr Scheiben Sie haben, desto runter ist sind die äußeren Ecken der Disc. Letztendlich enthält der vierte Parameter (32) die Anzahl der Ringe, aus der die Diskette besteht. Die Ringe sind ähnlich den Spuren einer Schallplatte. Kreise innerhalb von Kreisen. Diese Ringe unterteilen die Disk vom inneren Radius bis zum äußeren Radius und bringen mehr Detail. Nochmal, je mehr Unterteilungen es gibt, desto langsamer wird es laufen.

	case 2:							// Zeichne Objekt 3
		gluDisk(quadratic,0.5f,1.5f,32,32);		// Zeichne eine Disc (CD Form)
		break;						// Fertig

Unser viertes Objekt ist ein Objekt, von dem ich weiß, dass viele Schwierigkeiten hatten mit ihr hatten. Die Sphere (Kugel)! Diese ist relativ simpel. Der erste Parameter ist der Radius der Sphere. Für den Fall, dass Sie mit Radius/Diameter, etc. nicht vertraut sind, der Radius ist der Abstand vom Mittelpunkt des Objekts zum äußeren Rand des Objektes. In diesem Fall ist unser Radius 1.3f. Als nächstes haben wir unsere Unterteilungen um die Z-Achse "herum" (32) und unsere Unterteilungen "entlängs" der Z-Achse (32) Je mehr Unterteilungen Sie haben, desto glatter wir die Spehre aussehen. Spehren benötigen in der Regel recht wenig Unterteilungen, damit sie gut aussehen.

	case 3:							// Zeichne Objekt 4
		gluSphere(quadratic,1.3f,32,32);		// Zeichne eine Sphere
		break;						// Fertig

Unser fünftes Objekt wird erzeugt, in dem der selbe Befehl benutzt wird, den wir schon zur Erzeugung des Zylinders benutzt haben. Wenn Sie sich erinnern, als wir den Zylinder erzeugten kontrollierten die ersten beiden Parameter den Radius des Zylinders am Boden und an der Spitze. Um einen Kegel zu erhalten, klingt es logisch, dass wir den Radius an einem Ende einfach 0 setzen. Das erzeugt einen Punkt am entsprechenden Ende. Dementsprechend setzen wir den Radius der Spitze im unteren Code auf null. Das erzeugt unseren Punkt, wodurch auch unser Kegel erzeugt wird.

	case 4:							// Zeichne Objekt 5
		glTranslatef(0.0f,0.0f,-1.5f);			// Zentriere den Kegel
		gluCylinder(quadratic,1.0f,0.0f,3.0f,32,32);	// Ein Kegel mit unterem Raidus von 0.5f und einer Höhe von 2
		break;						// Fertig

Unser sechstes Objekt wird mit gluPartialDisc erzeugt. Das Objekt, dass wir mit diesem Befehl erzeugen, sieht genauso aus, wie die Disk, die wir vorhin erzeugt haben, mit dem Unterschied, dass der Befehl gluPartialDisc zwei neue Parameter hat. Der fünfte Parameter (part1) ist der Anfangs-Winkel ab wo wir die Disc zeichnen wollen. Der sechste Parameter ist der Sweep-Winkel. Der Sweep-Winkel ist die Distanz die wir vom aktuellen Winkel aus zurücklegen. Wir erhöhen den Sweep-Winkel, was ein langsames zunehmen im Uhrzeigersinn der Disc während des zeichnens, zur Folge hat. Wenn der Sweep-Winkel 360 Grad erreicht hat, erhöhen wir den Anfangs-Winkel, wodurch die Disc wieder langsam abnimmt. Danach starten wir erneut von vorne.

	case 5:							// Zeichne Objekt 6
		part1+=p1;					// Inkrementiere Start-Winkel
		part2+=p2;					// Inkrementiere Sweep-Winkel

		if(part1>359)					// 360 Grad
		{
			p1=0;					// Stoppe mit der Inkrementierung des Anfangs-Winkels
			part1=0;				// Setze Anfangs-Winkel auf 0
			p2=1;					// Beginne mit der Inkrementierung des Sweep-Winkels
			part2=0;				// Starte Sweep-Winkel bei 0
		}
		if(part2>359)					// 360 Grad
		{
			p1=1;					// Starte Inkrementierung des Anfangs-Winkel
			p2=0;					// Stoppe Inkrementierung des Sweep-Winkels
		}
		gluPartialDisk(quadratic,0.5f,1.5f,32,32,part1,part2-part1);	// Eine Disk  wie die davor
		break;						// Fertig
	};

	xrot+=xspeed;						// Inkrementiere Rotation auf der X-Achse
	yrot+=yspeed;						// Inkrementiere Rotation auf der Y-Achse
	return TRUE;						// Weiter geht's 
}
 

Im KillGLWindow() Codesegment müssen wir den Quadratic wieder löschen, um den Speicher freizugeben. Wir machen das mit dem Befehl gluDeleteQuadratic.

GLvoid KillGLWindow(GLvoid)					// Entferne das Fenster korrekt
{
	gluDeleteQuadric(quadratic);				// Lösche Quadratic - Gebe Ressourcen wieder frei

Nun zum letzten Teil, die Tasten-Eingabe. Fügen Sie das einfach da ein, wo wir den Rest der Tastatur-Eingabe überprüfen.

				if (keys[' '] && !sp)		// Wurde die Leertaste gedrückt?
				{
					sp=TRUE;		// Wenn ja, setze sp auf TRUE
					object++;		// wechsle durch die Objekte
					if(object>5)		// Ist object größer als 5?
						object=0;	// Wenn ja, dann auf 0 setzen
				}
				if (!keys[' '])			// Wurde die Leertaste wieder losgelassen
				{
					sp=FALSE;		// Wenn ja, dann setze sp auf FALSE
				}

Das war's! Nun können Sie Quadrics in OpenGL zeichnen. Einige wirklich beeindruckende Dinge können mit Morphing und Quadrics gemacht werden. Die animierte Disk ist ein Beispiel einfachen Morphens.